/* eslint-disable @typescript-eslint/no-this-alias */
/* eslint-disable prefer-rest-params */

const isMobile  = () =>  true;
const slideIcon = '';

interface Option {
  el: HTMLElement;
  startDeg?: number;
  endDeg?: number;
  outColor?: string;
  outLineWidth?: number;
  counterclockwise?: boolean;
  slider?: number;
  color?: string[]
  sliderBorderColor?: string
  value?: number
  change?: (v: any) => any
  textShow?: boolean
}

interface Position {
  x: number;
  y: number
}
export class DragAcr {
  el!: HTMLElement;
  startDeg = 0;
  endDeg = 1;
  outColor = '#FFF2C9';
  outLineWidth = 65
  counterclockwise = true
  slider = 10
  color: string[] = ['#FFBF00']
  sliderBorderColor = '#33aaff'
  value = 0
  change: (v: any) => any = (v) => { console.log(v); }
  textShow = true
  canvas!: HTMLCanvasElement
  width!: number;
  height!: number;
  circleWidth!: number;
  center!: number;
  radius!: number
  isDown!: boolean
  ctx!: CanvasRenderingContext2D
  animate!: number;
  P!: {
    x: number;
    y: number
  }

  img = new Image()

  constructor (param: Option) {
    this.img.src = slideIcon;

    this.initParam(param);
    this.img.onload = () => {
      this.draw(this.value);
    };
  }

  initParam (param: Option): void {
    const {
      el,
      startDeg = 0,
      endDeg = 1,
      outColor = '#FFF2C9',
      outLineWidth = 65,
      counterclockwise = true,
      slider = 10,
      color = ['#FFBF00'],
      sliderBorderColor = '#FFBF00',
      value = 0,
      change = (v) => { console.log(v); },
      textShow = true
    } = param;

    this.el = el;
    this.width = el.offsetWidth;
    this.height = el.offsetWidth;
    this.circleWidth = 420;
    this.center = el.offsetHeight / 2;
    this.radius = this.circleWidth / 2 - 30; // 滑动路径半径
    this.initCanvas(el);

    this.startDeg = startDeg;
    this.endDeg = endDeg;
    this.outColor = outColor;
    this.outLineWidth = outLineWidth;
    this.counterclockwise = counterclockwise;
    this.slider = slider;
    this.color = color;
    this.sliderBorderColor = sliderBorderColor;
    this.value = value;
    this.textShow = textShow;

    this.change = change;

    this.isDown = false;
    this.event(el);
  }

  initCanvas (dom: HTMLElement): void {
    this.canvas = document.createElement('canvas');
    this.canvas.setAttribute('id', 'dragArc');
    this.canvas.setAttribute('width', this.width + '');
    this.canvas.setAttribute('height', this.width + '');
    dom.appendChild(this.canvas);
    const ctx = this.canvas.getContext('2d');
    if (ctx) {
      this.ctx = ctx;
    }
  }

  // 绘图
  draw (value: number): void {
    this.ctx.clearRect(0, 0, this.width, this.height);
    this.ctx.save();

    const startDeg = this.counterclockwise ? Math.PI * (2 - this.startDeg) : Math.PI * this.startDeg;
    const endDeg = this.counterclockwise ? Math.PI * (2 - this.endDeg) : Math.PI * this.endDeg;

    // 绘制底部圆环
    this.ctx.globalCompositeOperation = 'source-out';
    this.ctx.beginPath();
    this.ctx.arc(this.center, this.center, this.radius, startDeg, endDeg, this.counterclockwise); // 绘制外侧圆弧
    this.ctx.strokeStyle = this.outColor;
    this.ctx.lineCap = 'round';
    this.ctx.lineWidth = this.outLineWidth;

    this.ctx.stroke();

    this.ctx.globalCompositeOperation = 'source-over';
    const Deg = this.valToDeg(value);

    // 绘制覆盖的圆环
    const themeColor = (typeof this.color === 'string') ? this.color : this.setLinearGradient();

    this.ctx.beginPath();
    this.ctx.arc(this.center, this.center, this.radius, startDeg, Deg, this.counterclockwise); // 可变圆弧
    this.ctx.strokeStyle = themeColor;
    this.ctx.lineCap = 'round';
    this.ctx.lineWidth = this.outLineWidth;
    this.ctx.stroke();

    this.ctx.shadowColor = '';
    this.ctx.shadowBlur = 0;
    this.P = this.DegToXY(Deg);
    this.ctx.drawImage(this.img, this.P.x - 86 / 2, this.P.y - 82 / 2, 86, 82);
  }

  // 将值转化为弧度
  valToDeg (v: number): number {
    const range = this.endDeg - this.startDeg;
    let val = range / 100 * v;
    if (this.counterclockwise && (val !== 0)) val = 2 - val;
    const startDeg = this.counterclockwise ? (2 - this.startDeg) : this.startDeg;
    return (startDeg + val) * Math.PI;
  }

  // 弧度转化为对应坐标值
  DegToXY (deg: number): Position {
    const d = 2 * Math.PI - deg;
    return this.respotchangeXY({
      x: this.radius * Math.cos(d),
      y: this.radius * Math.sin(d)
    });
  }

  // canvas坐标转化为中心坐标
  spotchangeXY (point: Position) : Position {
    const spotchangeX = (i: number) => {
      return i - this.center;
    };
    const spotchangeY = (i: number) => {
      return this.center - i;
    };
    return {
      x: spotchangeX(point.x),
      y: spotchangeY(point.y)
    };
  }

  // 中心坐标转化为canvas坐标
  respotchangeXY (point: Position): Position {
    const spotchangeX = (i: number) => {
      return i + this.center;
    };
    const spotchangeY = (i: number) => {
      return this.center - i;
    };
    return {
      x: spotchangeX(point.x),
      y: spotchangeY(point.y)
    };
  }

  setLinearGradient (): CanvasGradient {
    const grad = this.ctx.createLinearGradient(0, 0, 0, this.circleWidth);
    this.color.forEach((e, i) => {
      if (i === 0) {
        grad.addColorStop(0, e);
      } else if (i === this.color.length - 1) {
        grad.addColorStop(1, e);
      } else {
        grad.addColorStop(1 / this.color.length * (i + 1), e);
      }
    });
    return grad;
  }

  event (dom: HTMLElement): void { // 事件绑定
    if (isMobile()) {
      dom.addEventListener('touchstart', this.OnMouseDown.bind(this), false);
      dom.addEventListener('touchmove', this.throttle(this.OnMouseMove.bind(this)), false);
      dom.addEventListener('touchend', this.OnMouseUp.bind(this), false);
      return;
    }
    dom.addEventListener('mousedown', this.OnMouseDown.bind(this), false);
    dom.addEventListener('mousemove', this.throttle(this.OnMouseMove.bind(this)), false);
    dom.addEventListener('mouseup', this.OnMouseUp.bind(this), false);
  }

  OnMouseMove (evt: Event): void {
    if (!this.isDown) return;
    const evpoint = {} as Position;
    evpoint.x = this.getx(evt);
    evpoint.y = this.gety(evt);
    const point = this.spotchangeXY(evpoint);
    let deg = this.XYToDeg(point.x, point.y);
    if (typeof deg === 'number') {
      deg = this.counterclockwise ? deg : Math.PI * 2 - deg;
      let val = (deg / Math.PI - this.startDeg) / (this.endDeg - this.startDeg) * 100;
      if (val < 0) val = 100 + val;
      // if(val>100 || val<0) return;
      if (val >= 100) val = 100;
      if (val <= 0) val = 0;
      if (Math.abs(val - this.value) > 10) return;
      this.animate = requestAnimationFrame(this.draw.bind(this, val));
      if (this.value !== Math.round(val)) {
        this.value = Math.round(val);
        this.change(this.value);
      }
    }
  }

  OnMouseDown (evt: any): void {
    const range = 20;
    const X = this.getx(evt);
    const Y = this.gety(evt);
    const P = this.P;
    const minX = P.x - this.slider - range;
    const maxX = P.x + this.slider + range;
    const minY = P.y - this.slider - range;
    const maxY = P.y + this.slider + range;
    if (minX < X && X < maxX && minY < Y && Y < maxY) { // 判断鼠标是否在滑块上
      this.isDown = true;
    } else {
      this.isDown = false;
    }
  }

  OnMouseUp (): void { // 鼠标释放
    const _this = this;
    cancelAnimationFrame(_this.animate);
    this.isDown = false;
  }

  // 将坐标点转化为弧度
  XYToDeg (lx: number, ly: number): number {
    const adeg = Math.atan(ly / lx);
    let deg = 0;
    if (lx >= 0 && ly >= 0) {
      deg = adeg;
    }
    if (lx <= 0 && ly >= 0) {
      deg = adeg + Math.PI;
    }
    if (lx <= 0 && ly <= 0) {
      deg = adeg + Math.PI;
    }
    if (lx > 0 && ly < 0) {
      deg = adeg + Math.PI * 2;
    }
    return deg;
  }

  // 获取鼠标在canvas内坐标x
  getx (ev: any): number {
    if (!isMobile()) return ev.clientX - this.el.getBoundingClientRect().left;
    return ev.touches[0].clientX - this.el.getBoundingClientRect().left;
  }

  // 获取鼠标在canvas内坐标y
  gety (ev: any): number {
    if (!isMobile()) return ev.clientY - this.el.getBoundingClientRect().top;
    return ev.touches[0].clientY - this.el.getBoundingClientRect().top;
  }

  // 节流
  throttle (func: Function): () => any {
    let previous = 0;
    return function () {
      const now = Date.now();
      const context = this;
      const args = arguments;
      if (now - previous > 10) {
        func.apply(context, args);
        previous = now;
      }
    };
  }
}
